/////////////////////////////////////////////////////////////////////////
// $Id: pc_system.h 10209 2011-02-24 22:05:47Z sshwarts $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2002-2009  The Bochs Project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
//
/////////////////////////////////////////////////////////////////////////

#ifndef BX_PCSYS_H
#define BX_PCSYS_H

#include <vector>
#include <pthread.h>

#define BX_MAX_TIMERS 64
#define BX_NULL_TIMER_HANDLE 10000

typedef void (*bx_timer_handler_t)(void *);

/// @ forward declaration.
class bx_CPU_event;
class bxPageTransStampTable;
BOCHSAPI extern class bx_pc_system_c bx_pc_system;

#ifdef PROVIDE_M_IPS
extern double m_ips;
#endif

class BOCHSAPI bx_pc_system_c : private logfunctions {
private:

  // ===============================
  // Timer oriented private features
  // ===============================

  struct {
    bx_bool inUse;      // Timer slot is in-use (currently registered).
    Bit64u  period;     // Timer periodocity in cpu ticks.
    Bit64u  timeToFire; // Time to fire next (in absolute ticks).
    bx_bool active;     // 0=inactive, 1=active.
    bx_bool continuous; // 0=one-shot timer, 1=continuous periodicity.
    bx_timer_handler_t funct;  // A callback function for when the
                               //   timer fires.
    void *this_ptr;            // The this-> pointer for C++ callbacks
                               //   has to be stored as well.
#define BxMaxTimerIDLen 32
    char id[BxMaxTimerIDLen]; // String ID of timer.
  } timer[BX_MAX_TIMERS];

  unsigned   numTimers;  // Number of currently allocated timers.
  unsigned   triggeredTimer;  // ID of the actually triggered timer.
  Bit32u     currCountdown; // Current countdown ticks value (decrements to 0).
  Bit32u     currCountdownPeriod; // Length of current countdown period.
  Bit64u     ticksTotal; // Num ticks total since start of emulator execution.
  Bit64u     lastTimeUsec; // Last sequentially read time in usec.
  Bit64u     usecSinceLast; // Number of useconds claimed since then.

  // A special null timer is always inserted in the timer[0] slot.  This
  // make sure that at least one timer is always active, and that the
  // duration is always less than a maximum 32-bit integer, so a 32-bit
  // counter can be used for the current countdown.
  static const Bit64u NullTimerInterval;
  static void nullTimer(void* this_ptr);

#if !defined(PROVIDE_M_IPS)
  // This is the emulator speed, as measured in millions of
  // x86 instructions per second that it can emulate on some hypothetically
  // nomimal workload.
  double     m_ips; // Millions of Instructions Per Second
#endif

  // This handler is called when the function which decrements the clock
  // ticks finds that an event has occurred.
  void   countdownEvent(void);

public:
  // ========================================
  // Some CPU specific options for SMP emulation.
  // ========================================
  volatile Bit32u running;   // CPUs pools on this before starting.

public:
  // ========================================
  // emulator debugging shared memory.
  // ========================================
  void *debug;
  pthread_mutex_t debug_lock;

public:
  // ========================================
  // Some CPU specific options for fast query.
  // ========================================
  bx_bool parallel;       // CPU executing in parallel.
  bx_bool nolink;         // No trace link.   
  bx_bool nocache;        // No cache.
  bx_bool instrlock;      // Hold lock before every instruction.
  bx_bool privcache;      // Private cache.
  bx_bool notrace;        // No trace.
  bx_bool nojit;          // No JIT.
  bx_bool lockcheck;      // Lock check.
  bx_bool systemcheck;    // System check.
  Bit32u ncpus;           // Number of processors.
  Bit32u privcount;       // Threshold to promote to private cache.
  Bit32u interp_instrmax; // Max number of instructions the interpreter will build.
  Bit64u nocache_min;     // The minimum address to flush;
  Bit64u nocache_max;     // The maximum address to flush;
  Bit64u lock_icount;     // The frequent of page lock check.
  Bit64u icount;          // Interpreation count to compilation.
  Bit64u instrmax;        // Max instr number in a basicblock.
  Bit64u jit_min;         // Minimum address range to JIT.
  Bit64u jit_max;         // Maximum address range to JIT.

public:
  // ========================================
  // The field offset in the BX_CPU_C struct.
  // ========================================
  Bit32u find_field_offset_by_name(const char* fieldname);
  Bit32u find_reg_offset_by_index(Bit32u index, Bit32u type);

public:
  // ========================================
  // Atomic instruction emulation.
  // ========================================
  /// -------------------------------------------------------- ///
  //  Simple method to support the emulation of atomic          //
  //  instructions.                                             //
  //  When an atomic instruction is about to be emulated.       //
  //  it holds a write lock on the lock_instr_mutex. when       //
  //  normal instructions are about to be emulated. they        //
  //  hold a read lock.                                         //
  //  This leave normal instructions with parallelism           //
  //  and lock instructions with correctness.                   //
  /// -------------------------------------------------------- ///
  /// @ mutex used to guarantee atomicity for lock instructions.
  pthread_rwlock_t lock_instr_mutex;

  /// @ get read lock to the execution unit hash tables.
  BX_CPP_INLINE void acquire_instr_rdlock(void) 
  {
    pthread_rwlock_rdlock(&lock_instr_mutex);
  }

  /// @ get write lock to the execution unit hash tables.
  BX_CPP_INLINE void acquire_instr_wrlock(void)
  {
    pthread_rwlock_wrlock(&lock_instr_mutex);
  }

  /// @ release the lock to the execution unit hash tables.
  BX_CPP_INLINE void release_instr_rwlock(void)
  {
    pthread_rwlock_unlock(&lock_instr_mutex);
  }

  /// -------------------------------------------------------- ///
  /// page locking to support atomic instruction emulation     ///
  /// -------------------------------------------------------- ///
  bxPageTransStampTable *pageTLBTransTable;   

public:
  // ==============================
  // CPU functions 
  // ==============================
  /// @ return the CPU ID of the current executing thread.
  Bit32u getCurrentCpuID();

public:

  // ==============================
  // Timer oriented public features
  // ==============================

  void   initialize(Bit32u ips);
  int    register_timer(void *this_ptr, bx_timer_handler_t, Bit32u useconds,
                         bx_bool continuous, bx_bool active, const char *id);
  bx_bool unregisterTimer(unsigned timerID);
  void   start_timers(void);
  void   activate_timer(unsigned timer_index, Bit32u useconds, bx_bool continuous);
  void   deactivate_timer(unsigned timer_index);
  unsigned triggeredTimerID(void) {
    return triggeredTimer;
  }
  static BX_CPP_INLINE void tick1(void) {
    if (--bx_pc_system.currCountdown == 0) {
      bx_pc_system.countdownEvent();
    }
  }
  static BX_CPP_INLINE void tickn(Bit32u n) {
    while (n >= bx_pc_system.currCountdown) {
      n -= bx_pc_system.currCountdown;
      bx_pc_system.currCountdown = 0;
      bx_pc_system.countdownEvent();
      // bx_pc_system.currCountdown is adjusted to new value by countdownevent().
    }
    // 'n' is not (or no longer) >= the countdown size.  We can just decrement
    // the remaining requested ticks and continue.
    bx_pc_system.currCountdown -= n;
  }

  int register_timer_ticks(void* this_ptr, bx_timer_handler_t, Bit64u ticks,
                           bx_bool continuous, bx_bool active, const char *id);
  void activate_timer_ticks(unsigned index, Bit64u instructions,
                            bx_bool continuous);
  Bit64u time_usec();
  Bit64u time_usec_sequential();
  static BX_CPP_INLINE Bit64u time_ticks() {
    return bx_pc_system.ticksTotal +
      Bit64u(bx_pc_system.currCountdownPeriod - bx_pc_system.currCountdown);
  }

  Bit64u cpus_ticks(void);

  static BX_CPP_INLINE Bit32u  getNumCpuTicksLeftNextEvent(void) {
    return bx_pc_system.currCountdown;
  }
#if BX_DEBUGGER
  static void timebp_handler(void* this_ptr);
#endif
  static void benchmarkTimer(void* this_ptr);

  // ===========================
  // Non-timer oriented features
  // ===========================

  bx_bool HRQ;     // Hold Request

  // Address line 20 control:
  //   1 = enabled: extended memory is accessible
  //   0 = disabled: A20 address line is forced low to simulate
  //       an 8088 address map
  bx_bool enable_a20;

  // start out masking physical memory addresses to:
  //   8086:      20 bits
  //    286:      24 bits
  //    386:      32 bits
  // when A20 line is disabled, mask physical memory addresses to:
  //    286:      20 bits
  //    386:      20 bits
  bx_phy_address a20_mask;

  volatile bx_bool kill_bochs_request;

  void set_HRQ(bx_bool val);  // set the Hold ReQuest line
  void set_INTR(bx_bool value); // set the INTR line to value

  // Cpu and System Reset
  int Reset(unsigned type);
  Bit8u  IAC(void);

  bx_pc_system_c();

  Bit32u  inp(Bit16u addr, unsigned io_len) BX_CPP_AttrRegparmN(2);
  void    outp(Bit16u addr, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3);
  void    set_enable_a20(bx_bool value);
  bx_bool get_enable_a20(void);
  void    MemoryMappingChanged(void); // flush TLB in all CPUs
  void    invlpg(bx_address addr);    // flush TLB page in all CPUs
  void    exit(void);
  void    register_state(void);
};

#endif
